package com.sjft.cloud.extension.generate;

import com.sjft.cloud.extension.generate.constant.CustomConstVal;
import com.sjft.cloud.extension.generate.engine.CustomVelocityTemplateEngine;
import cn.hutool.core.io.FileUtil;
import com.sjft.cloud.extension.generate.properties.JdbcConfigProperties;
import com.sjft.cloud.extension.generate.properties.OutFileConfigProperties;
import com.sjft.cloud.extension.generate.properties.TableConfigProperties;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.dom4j.*;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.xml.sax.SAXException;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * GeneratorMojo
 *
 * @author Jin
 */
@Slf4j
@Mojo(name = "generator")
public class GeneratorMojo extends AbstractMojo {
    private final static String API_MODULE_POM_TEMPLATE = CustomConstVal.TEMPLATE_API_POM + ".vm";
    private final static String API_SPRING_FACTORIES = CustomConstVal.TEMPLATES_SPRING_FACTORIES + ".vm";

    private final static String APP_MODULE_POM_TEMPLATE = CustomConstVal.TEMPLATE_APP_POM + ".vm";
    private final static String APP_APPLICATION_JAVA_TEMPLATE = CustomConstVal.TEMPLATE_APPLICATION_JAVA + ".vm";
    private final static String APP_APPLICATION_PROPERTIES_TEMPLATE = CustomConstVal.TEMPLATE_APPLICATION_PROPERTIES + ".vm";
    private final static String APP_BOOTSTRAP_PROPERTIES = CustomConstVal.TEMPLATES_BOOTSTRAP_PROPERTIES + ".vm";


    @Parameter(property = "project.build.directory", required = true)
    private File outputDirectory;

    @Parameter(property = "project.build.sourceDirectory", required = true, readonly = true)
    private File sourceDir;

    @Parameter(property = "project.packaging", readonly = true)
    private String packaging;

    @Parameter(property = "project.basedir", required = true, readonly = true)
    private File baseDir;

    @Parameter(property = "project.artifactId", required = true, readonly = true)
    private String artifactId;

    @Parameter(property = "project.groupId", required = true, readonly = true)
    private String groupId;

    @Parameter(property = "project.version", required = true, readonly = true)
    private String version;

    @Parameter(property = "packageName", defaultValue = "com.sjft")
    private String packageName;

    @Parameter(property = "moduleName", required = true, readonly = true)
    private String moduleName;

    @Parameter(property = "author", defaultValue = "jin")
    private String author;

    @Parameter(property = "outFileConfig")
    private OutFileConfigProperties outFileConfig;

    @Parameter(property = "jdbcConfig", required = true)
    private JdbcConfigProperties jdbcConfig;

    @Parameter(property = "tableConfig", required = true)
    private TableConfigProperties tableConfig;

    private String getSourcePath(String path) {
        return String.format("%s" + File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator, path);
    }

    private String getOutputPath(String path) {
        return String.format("%s" + File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator, path);
    }


    public void execute() throws MojoExecutionException {
        try {
            if (StringUtils.isBlank(packaging)) {
                log.info("此模块{} -  <packaging></packaging> 未找到！", artifactId);
                return;
            }
            if (!StringUtils.equals(packaging, "pom")) {
                log.info("此模块 {} -  <packaging>{}</packaging> 不正确，生成代码终止！", artifactId, packaging);
                return;
            }

            AbstractTemplateEngine templateEngine = new CustomVelocityTemplateEngine();

            String apiModuleName = artifactId + "-" + "api";
            String appModuleName = artifactId + "-" + "app";

            //POM文件
            String currentPomFile = baseDir.getAbsolutePath() + File.separator + "pom.xml";
            String apiModulePomFile = baseDir.getAbsolutePath() + File.separator + apiModuleName + File.separator + "pom.xml";
            String appModulePomFile = baseDir.getAbsolutePath() + File.separator + appModuleName + File.separator + "pom.xml";

            //检查并创建目录
            String apiJavaFileOutPutDir = getOutputPath(baseDir.getAbsolutePath() + File.separator + apiModuleName);
            if (!FileUtil.exist(apiJavaFileOutPutDir)) {
                FileUtil.mkdir(apiJavaFileOutPutDir);
            }
            String appJavaFileOutPutDir = getOutputPath(baseDir.getAbsolutePath() + File.separator + appModuleName);
            if (!FileUtil.exist(appJavaFileOutPutDir)) {
                FileUtil.mkdir(appJavaFileOutPutDir);
            }

            //创建子模块POM
            Map<String, Object> data = new HashMap<>();
            data.put("parentArtifactId", artifactId);
            data.put("apiArtifactId", apiModuleName);
            data.put("appArtifactId", appModuleName);
            data.put("app", packageName + "." + moduleName + "." + "app");
            data.put("appPath", (packageName + "." + moduleName + "." + "app").replace(".","/"));

            templateEngine.init(null);
            templateEngine.writer(data, API_MODULE_POM_TEMPLATE, apiModulePomFile);
            templateEngine.writer(data, APP_MODULE_POM_TEMPLATE, appModulePomFile);

            //更新POM
            checkAndUpdatePomXml(currentPomFile, apiModuleName, appModuleName);

            //创建Api资源目录
            String apiMetaInfoDir = getSourcePath(baseDir.getAbsolutePath() + File.separator + apiModuleName) + File.separator + "META-INF";
            if (!FileUtil.exist(apiMetaInfoDir)) {
                FileUtil.mkdir(apiMetaInfoDir);
            }

            //创建App资源目录
            String appResourceDir = getSourcePath(baseDir.getAbsolutePath() + File.separator + appModuleName);
            if (!FileUtil.exist(appResourceDir)) {
                FileUtil.mkdir(appResourceDir);
            }

            //生成Api模块代码
            templateEngine.writer(data,API_SPRING_FACTORIES , apiMetaInfoDir + File.separator + "spring.factories");

            //生成App模块代码
            templateEngine.writer(data, APP_BOOTSTRAP_PROPERTIES, appResourceDir + File.separator + "bootstrap.properties");
            templateEngine.writer(data, APP_APPLICATION_PROPERTIES_TEMPLATE, appResourceDir + File.separator + "application.properties");

            //开始生成代码
            GeneratorBuilder generatorBuilder = GeneratorBuilder.build()
                    .injectionConfig()
                    .dataSourceConfig(jdbcConfig.getUrl(), jdbcConfig.getDriverName(), jdbcConfig.getUser(), jdbcConfig.getPassword())
                    .packageConfig(packageName, StringUtils.isBlank(moduleName) ? artifactId.replace("-", ".") : moduleName)
                    .strategyConfig(tableConfig.getTablePrefix(), tableConfig.getTables().toArray(new String[0]), tableConfig.getEnableSwagger(), true)
                    .globalConfig(
                            apiJavaFileOutPutDir,
                            appJavaFileOutPutDir,
                            outFileConfig,
                            false,
                            tableConfig.getEnableSwagger(),
                            tableConfig.getEnableBaseResultMap(),
                            tableConfig.getEnableBaseColumnList(),
                            outFileConfig.getOverrideFile(),
                            DateType.TIME_PACK, author)
                    .setTemplateEngine(templateEngine);
            generatorBuilder.execute();

            //生成App启动类代码
            templateEngine.writer(data, APP_APPLICATION_JAVA_TEMPLATE, appJavaFileOutPutDir + data.get("app").toString().replace(".", File.separator) + File.separator + "Application.java");


        } catch (Exception e) {
            log.error("代码生成出错！", e);
            throw new MojoExecutionException("unable to generator codes of table.", e);
        }

    }

    private void checkAndUpdatePomXml(String pomFileDir, String apiMavenModelName, String appMavenModelName) throws DocumentException, IOException, SAXException {
        SAXReader reader = new SAXReader();
        reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        Document pomXmlDocument = reader.read(pomFileDir);
        //漂亮的格式
        OutputFormat outputFormat = OutputFormat.createPrettyPrint();
        if (pomXmlDocument != null) {
            Element rootElement = pomXmlDocument.getRootElement();
            if (rootElement.elements("modules").size() < 1) {
                Element modulesElement = DocumentHelper.createElement(QName.get("modules", rootElement.getNamespace()));
                rootElement.add(modulesElement);
                appendChildModule(apiMavenModelName, appMavenModelName, rootElement, modulesElement);
            } else {
                Element modulesElement = rootElement.element("modules");
                List moduleElements = modulesElement.elements("module");
                if (moduleElements.size() > 0) {
                    for (Object moduleElement : moduleElements) {
                        if (moduleElement instanceof Element) {
                            Element element = (Element) moduleElement;
                            log.info("找到{}模块 - {}", element.getName(), element.getText());
                            if (element.getText().equals(apiMavenModelName) || element.getText().equals(appMavenModelName)) {
                                modulesElement.remove(element);
                            }
                        }
                    }
                    appendChildModule(apiMavenModelName, appMavenModelName, rootElement, modulesElement);
                }
            }

            XMLWriter output = new XMLWriter(new FileWriter(pomFileDir), outputFormat);
            output.write(rootElement);
            output.close();
        }
    }

    private void appendChildModule(String apiMavenModelName, String appMavenModelName, Element rootElement, Element modulesElement) {
        Element apiModelElement = DocumentHelper.createElement(QName.get("module", rootElement.getNamespace()));
        apiModelElement.addText(apiMavenModelName);
        modulesElement.add(apiModelElement);

        Element appModelElement = DocumentHelper.createElement(QName.get("module", rootElement.getNamespace()));
        appModelElement.addText(appMavenModelName);
        modulesElement.add(appModelElement);
    }

}
